package com.wzdigit.wms.raw.service.gdn;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wzdigit.framework.Shift;
import com.wzdigit.framework.utils.RedisUtil;
import com.wzdigit.mes.basic.api.BasicParamApi;
import com.wzdigit.mes.basic.api.BasicSimpleCodeRuleApi;
import com.wzdigit.mes.basic.dto.ParamDetailDTO;
import com.wzdigit.wms.basic.client.request.outstock.CancelPickMaterialRequest;
import com.wzdigit.wms.basic.client.response.Result;
import com.wzdigit.wms.basic.client.response.outstock.ScanResponse;
import com.wzdigit.wms.basic.domain.asn.AsnDtl;
import com.wzdigit.wms.basic.domain.asn.AsnHeader;
import com.wzdigit.wms.basic.domain.basic.Sku;
import com.wzdigit.wms.basic.domain.dtos.inventory.UpdateInventoryInputDto;
import com.wzdigit.wms.basic.domain.inventory.Goods;
import com.wzdigit.wms.basic.domain.inventory.GoodsHis;
import com.wzdigit.wms.basic.domain.inventory.GoodsLog;
import com.wzdigit.wms.basic.domain.inventory.InvTrans;
import com.wzdigit.wms.basic.domain.order.TrsnDtl;
import com.wzdigit.wms.basic.domain.order.TrsnDtlGoods;
import com.wzdigit.wms.basic.domain.order.TrsnHeader;
import com.wzdigit.wms.basic.domain.transfer.GdnDtl;
import com.wzdigit.wms.basic.domain.transfer.GdnDtlGoods;
import com.wzdigit.wms.basic.domain.transfer.GdnHeader;
import com.wzdigit.wms.basic.enums.*;
import com.wzdigit.wms.basic.enums.log.GoodsLogEnum;
import com.wzdigit.wms.basic.enums.operate.OperateCodeEnum;
import com.wzdigit.wms.basic.mapper.core.basic.SkuMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsHisMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsLogMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsMapper;
import com.wzdigit.wms.basic.mapper.core.order.*;
import com.wzdigit.wms.basic.mapper.core.transfer.GdnDtlGoodsMapper;
import com.wzdigit.wms.basic.mapper.core.transfer.GdnDtlMapper;
import com.wzdigit.wms.basic.mapper.core.transfer.GdnHeaderMapper;
import com.wzdigit.wms.basic.service.code.InventoryService;
import com.wzdigit.wms.basic.util.HttpSessionUtil;
import com.wzdigit.wms.raw.client.response.gdn.delivery.*;
import com.wzdigit.wms.raw.client.response.gdn.picktask.CancelPickResponse;
import com.wzdigit.wms.raw.client.response.gdn.picktask.PickAllQty;
import com.wzdigit.wms.raw.exception.SystemException;
import com.wzdigit.wms.raw.mapper.gdn.DeliveryNewMapper;
import com.wzdigit.wms.raw.mapper.gdn.PickTaskMapper;
import com.wzdigit.wms.raw.service.backtosap.*;
import com.wzdigit.wms.web.service.wms2sap.wmsbillstatus.ZDT_076_ReqItems;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.core.utils.BeanUtil;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 交接确认PDA【新】
 * Created by zyf on 2021/8/8 13:02
 */
@Service
@Slf4j
public class DeliveryNewService {
    private List<Integer> dtlStatusList= Arrays.asList(GdnDtlStatusEnum.CANCEL.code,
            GdnDtlStatusEnum.SHIPMENTED.code,GdnDtlStatusEnum.CLOSE.code);

    private static final String TRSN_ORDER = "TRSN_ORDER";

    @Autowired
    private GdnHeaderMapper gdnHeaderMapper;
    @Autowired
    private GdnDtlMapper gdnDtlMapper;
    @Autowired
    private GdnDtlGoodsMapper gdnDtlGoodsMapper;
    @Autowired
    private DeliveryNewMapper deliveryMapper;
    @Autowired
    private GoodsMapper goodsMapper;
    @Lazy
    @Autowired
    private TrsnHeaderMapper trsnHeaderMapper;
    @Lazy
    @Autowired
    private TrsnDtlMapper trsnDtlMapper;
    @Lazy
    @Autowired
    private TrsnDtlGoodsMapper trsnDtlGoodsMapper;
    @Lazy
    @Autowired
    private GoodsLogMapper goodsLogMapper;
    @Lazy
    @Autowired
    private GoodsHisMapper goodsHisMapper;
    @Autowired
    private SkuMapper skuMapper;
    @Autowired
    private AsnHeaderMapper asnHeaderMapper;
    @Autowired
    private AsnDtlMapper asnDtlMapper;
    @Autowired
    private PickTaskMapper pickTaskMapper;


    @Lazy
    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private DeliveryWriteOffService deliveryWriteOffService;

    @Autowired
    OutStoreBackService outStoreBackService;

    @Autowired
    PurchaseOrderBackService purchaseOrderBackService;

    @Autowired
    GetAndReturnMaterialBackService getAndReturnMaterialBackService;

    @Autowired
    WaitCheckBackService waitCheckBackService;

    @Autowired
    private WmsBillStatusBackService wmsBillStatusBackService;



    @Reference(version = "${dubbo.consumers.basic.simple.code.rule.api:1.0.0}", check = false)
    private BasicSimpleCodeRuleApi basicSimpleCodeRuleApi;

    /*
        交接确认提交功能：前段验证扫描数是否与订单数相等
        1.发货单头状态为0(取消)或4(已完成)或5(已关闭)，报错
        2.根据gdnNo获取缓存的要交接确认的SN信息
        3.发货单所有明细的状态0(取消)或7(已发运)或8(已关闭)，报错
        4.交接功能
            （1）生成入库凭证单头
            （2）生成入库单明细+货品
            （3）更新库位库存&库区库存
            （4）写SN操作日志
            （5）将SN从GOODS表删除移到GOODS_HIS表【置空WHS_ID&LOC_ID】
            （6）更新发货单主档状态为4(已完成)
            （7）更新发货单明细的发货数为订单数，状态为4(已完成)
            （8）更新发货单货品状态为2(已出库)
            （9）清除缓存

        TODO 特定单据类型出库是自动生成入库单
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result<?> deliveryCommit(String gdnNo, BigDecimal gdnHeaderId,
                                    HttpServletRequest servletRequest, HttpServletResponse servletResponse)
            throws Exception{
        GdnHeader gdnHeader = gdnHeaderMapper.selectOne(new QueryWrapper<GdnHeader>(new GdnHeader().setGdnNo(gdnNo)));
        if (gdnHeader==null){
            Shift.fatal("请选择正确的出库单!");
        }
        if (gdnHeader.getStatus()==null){
            Shift.fatal("出库单["+gdnNo+"]状态错误,不可进行出库交接确认!");
        }
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CANCEL.code)){
            Shift.fatal("出库单["+gdnNo+"]状态为["+GdnHeaderStatusEnum.CANCEL.enumName+"],不可进行出库交接确认!");
        }
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.FINISH.code)){
            Shift.fatal("出库单["+gdnNo+"]状态为["+GdnHeaderStatusEnum.FINISH.enumName+"],不可进行出库交接确认!");
        }
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CLOSE.code)){
            Shift.fatal("出库单["+gdnNo+"]状态为["+GdnHeaderStatusEnum.CLOSE.enumName+"],不可进行出库交接确认!");
        }

        //缓存中要交接的SN
        List<String> snCacheList = this.getGoodsListCache(gdnNo);
        if (CollectionUtil.isEmpty(snCacheList)){
            Shift.fatal("请扫描SN再提交！");
        }
        //查询条码信息
        List<Goods> goodsList = goodsMapper.selectList(new QueryWrapper<Goods>().in("GOODS_SN", snCacheList));
        if (CollectionUtil.isEmpty(goodsList)){
            Shift.fatal("请扫描正确的SN！");
        }
        if (snCacheList.size()!=goodsList.size()){
            Shift.fatal("请扫描正确的SN！");
        }
        for (Goods goods : goodsList) {
            if (!goods.getStatus().equals(GoodsStatusEnum.HAS_DOWN.code)){
                Shift.fatal(goods.getGoodsSn()+"状态非["+GoodsStatusEnum.HAS_DOWN.enumName+"]，无法交接!");
            }

        }

        Boolean oneceLimitFlag=false;//true为需要限制一次出库不允许短收
        Boolean oneceNotLimitFlag=false;//true为需要限制一次出库允许短收，短收后同时更新入库单主动明细为完成
        Boolean asnFlag=false;//true需要生成入库单

        switch (gdnHeader.getGdnType()){
            case "D12" : //委外领料 一次过账
                oneceNotLimitFlag=true;
                break;
            case "D16": //报废出库 一次过账
                oneceNotLimitFlag=true;
                break;
            case "D17":  //销售出库(供应商) 一次过账
                oneceNotLimitFlag=true;
                break;
            case "D21": //易损件领料单 一次过账
                oneceNotLimitFlag=true;
                break;
            case "D22": //不合格凭证退货 一次过账
                oneceLimitFlag=true;
                break;
            case "D29" : //不同库位调拨(出) 一次过账
                oneceNotLimitFlag=true;
                asnFlag=true;
                break;

            case "D26" : //不同库位调拨(出) 一次过账同时生成入库单
                asnFlag=true;
                oneceLimitFlag=true;
                break;
            case "D27" : //生产领料(单次) 一次过账同时生成入库单
                asnFlag=true;
                oneceLimitFlag=true;
                break;
            case "D28" : //生产领料(单次) 一次过账
                oneceNotLimitFlag=true;
                break;

            //TODO 新加的
            case "D24" : //不同库位调拨(出) 一次过账同时生成入库单
                oneceLimitFlag=true;
                break;
            case "D25" : //生产领料(单次) 一次过账同时生成入库单
                oneceLimitFlag=true;
                break;
        }

        //校验是否能一次过账
        if (oneceLimitFlag){
            //查询所有的明细行
            BigDecimal commitQty = goodsList.stream().map(Goods::getQty)
                    .reduce(BigDecimal.ZERO,BigDecimal::add);
            List<GdnDtl> dtlList = gdnDtlMapper.selectList(new QueryWrapper<GdnDtl>().eq("GDN_HEADER_ID", gdnHeader.getGdnHeaderId()));
            BigDecimal orderQty = dtlList.stream().map(GdnDtl::getOrderQty)
                    .reduce(BigDecimal.ZERO,BigDecimal::add);
            if(commitQty.compareTo(orderQty) != 0){
                Shift.fatal("出库数需要等于需求数!");
            }
        }



        //查询交接的GDN_DTL_GOODS
        List<DeliveryGoodsResponse> gdnDtlGoodsList = deliveryMapper.selectDeliveryGoodsList(gdnHeaderId, snCacheList);
        List<BigDecimal> dtlIdList = gdnDtlGoodsList.stream().map(t -> t.getGdnDtlId()).distinct().collect(Collectors.toList());
        //查询交接的GDN_DTL`
        List<SimpleGdnDtl> gdnDtls = deliveryMapper.selectGdnDtlList(gdnHeaderId,dtlIdList);
        for (SimpleGdnDtl gdnDtl : gdnDtls) {
            if (gdnDtl.getStatus()==null){
                Shift.fatal("出库单["+gdnNo+"]明细行号["+gdnDtl.getGdnLineNo()+"]、料号["+gdnDtl.getSkuCode()
                        +"]状态错误，不可进行交接确认!");
            }
            if (gdnDtl.getStatus().equals(GdnDtlStatusEnum.CANCEL.code)){
                Shift.fatal("出库单["+gdnNo+"]明细行号["+gdnDtl.getGdnLineNo()+"]、料号["+gdnDtl.getSkuCode()
                        +"]状态为["+GdnDtlStatusEnum.CANCEL.enumName+"]，不可进行交接确认!");
            }
            if (gdnDtl.getStatus().equals(GdnDtlStatusEnum.SHIPMENTED.code)){
                Shift.fatal("出库单["+gdnNo+"]明细行号["+gdnDtl.getGdnLineNo()+"]、料号["+gdnDtl.getSkuCode()
                        +"]状态为["+GdnDtlStatusEnum.SHIPMENTED.enumName+"]，不可进行交接确认!");
            }
            if (gdnDtl.getStatus().equals(GdnDtlStatusEnum.CLOSE.code)){
                Shift.fatal("出库单["+gdnNo+"]明细行号["+gdnDtl.getGdnLineNo()+"]、料号["+gdnDtl.getSkuCode()
                        +"]状态为["+GdnDtlStatusEnum.CLOSE.enumName+"]，不可进行交接确认!");
            }
        }
        String loginName = HttpSessionUtil.getLoginName(servletRequest);

        Map<String, List<Goods>> trsnGoodsMap = goodsList.stream().collect(Collectors.groupingBy(Goods::getGoodsSn));
        Map<BigDecimal, List<DeliveryGoodsResponse>> trsnDtlGoodsMap = gdnDtlGoodsList.stream().collect(Collectors.groupingBy(DeliveryGoodsResponse::getGdnDtlId));

        //记录出库凭证
        Date trsnDate = new Date();
        TrsnHeader trsnHeader = this.setTrsnHeader(gdnHeader, gdnDtls.get(0), loginName);
        trsnHeaderMapper.insert(trsnHeader);
        TrsnDtl trsnDtl=new TrsnDtl();
        TrsnDtlGoods trsnDtlGoods=new TrsnDtlGoods();
        String trsnGoodsSn;
        Goods trsnGoods=new Goods();
        int count=0;
        for (SimpleGdnDtl gdnDtl : gdnDtls) {
            List<DeliveryGoodsResponse> gdnDtlGoods = trsnDtlGoodsMap.get(gdnDtl.getGdnDtlId());
            trsnDtl = this.setTrsnDtl(gdnHeader, gdnDtl, loginName, trsnHeader,count);
            trsnDtl.setQty(gdnDtlGoods.stream()
                    .map(DeliveryGoodsResponse::getQty)
                    .reduce(BigDecimal.ZERO,BigDecimal::add));
            trsnDtlMapper.insert(trsnDtl);
            for (DeliveryGoodsResponse gdnDtlGood : gdnDtlGoods) {
                trsnGoodsSn=gdnDtlGood.getGoodsSn();
                trsnGoods=trsnGoodsMap.get(trsnGoodsSn).get(0);
                trsnDtlGoods.setTrsnDtlId(trsnDtl.getTrsnDtlId());
                trsnDtlGoods.setGoodsSn(trsnGoodsSn);
                trsnDtlGoods.setGoodsId(trsnGoods.getGoodsId());
                trsnDtlGoods.setQty(gdnDtlGood.getQty());
                trsnDtlGoods.setQaStatus(trsnGoods.getQaStatus());
                trsnDtlGoods.setComments("["+gdnNo+"]下明细行["+gdnDtl.getGdnDtlId()+"]出库");
                trsnDtlGoods.setStatus(trsnGoods.getStatus());
                trsnDtlGoods.setCreateUser(loginName);
                trsnDtlGoods.setCreateTime(trsnDate);
                trsnDtlGoods.setModifyUser(loginName);
                trsnDtlGoods.setModifyTime(trsnDate);
                trsnDtlGoodsMapper.insert(trsnDtlGoods);
            }
            count++;
        }

        //减少库存
        Map<String, List<Goods>> updateInvMap = goodsList.stream().collect(
                Collectors.groupingBy(t -> t.getWhsId() + "#" + t.getLocId() + "#" + t.getOwner() + "#" +
                        t.getSkuId() + "#" + t.getFactory() + "#" + t.getVendorId() + "#" + t.getLotno()
                        + "#" + t.getInvAttr() + "#" + t.getQaStatus(), Collectors.toList()));
        UpdateInventoryInputDto inventoryInputDto= new UpdateInventoryInputDto();
        for (Map.Entry<String, List<Goods>> entry : updateInvMap.entrySet()) {
            inventoryInputDto = this.setInventoryInputDto(entry.getValue(), loginName);
            inventoryService.updateLocInventroy(inventoryInputDto);
            inventoryService.updateZoneInventory(inventoryInputDto);
        }

        Date logDate=new Date();
        //将goods移入历史表并记录日志
        GoodsLog goodsLog=new GoodsLog();
        GoodsHis goodsHis=new GoodsHis();
        for (Goods goods : goodsList) {
            //写SN操作日志
            goodsLog= this.setGoodsLog(gdnNo, loginName, logDate, goods);
            goodsLogMapper.insert(goodsLog);
            //添加SN到HIS表
            BeanUtil.copyNonNull(goods, goodsHis);
            goodsHis.setLogCreateUser(loginName);
            goodsHis.setLogCreateTime(logDate);
            goodsHis.setStatus(GoodsStatusEnum.HAS_CHUKU.code);
            goodsHis.setWhsId(null);
            goodsHis.setLocId(null);
            goodsHis.setModifyTime(new Date());
            goodsHis.setModifyUser(loginName);
            goodsHisMapper.insert(goodsHis);
        }
        //TODO 待确定单据类型修改判断逻辑
        //TODO 入库单仓库逻辑待定&入库单类型待定
        if (asnFlag){
            List<DeliveryGoodsResponse> asndtlGoods=new ArrayList<>();
            AsnHeader asnHeader=new AsnHeader();
            List<GdnDtl> asnGdnList = gdnDtlMapper.selectList(new QueryWrapper<GdnDtl>().in("GDN_DTL_ID", dtlIdList));
            AsnDtl asnDtl=new AsnDtl();
            int dtlCount=1;
            Date asnDate=new Date();
            setAsnHeader(asnHeader,gdnHeader,loginName,new Date(),gdnNo,gdnHeader.getGdnType(),
                    trsnDtlGoodsMap.get(gdnDtls.get(0).getGdnDtlId()));
            asnHeaderMapper.insert(asnHeader);
            for (GdnDtl gdnDtl : asnGdnList) {
                setAsnDtl(asnHeader,asnDtl,dtlCount,loginName,gdnNo,asnDate,gdnDtl,gdnHeader);
                asnDtlMapper.insert(asnDtl);
                dtlCount++;
            }


        }

        //删除GOODS表的条码
        List<Integer> deleteGoodsIds = goodsList.stream().map(t -> t.getGoodsId()).collect(Collectors.toList());
        QueryWrapper<Goods> delectGoodsQueryWrapper=new QueryWrapper<>();
        delectGoodsQueryWrapper.in("GOODS_ID",deleteGoodsIds);
        goodsMapper.delete(delectGoodsQueryWrapper);

        //更新出库主档、明细、货品状态【待修改】
        Map<BigDecimal, List<SimpleGdnDtl>> dtlCollectMap = gdnDtls.stream().collect(Collectors.groupingBy(SimpleGdnDtl::getGdnDtlId));
        Map<BigDecimal, List<DeliveryGoodsResponse>> goodsCollectMap = gdnDtlGoodsList.stream().collect(Collectors.groupingBy(DeliveryGoodsResponse::getGdnDtlId));
        SimpleGdnDtl gdnDtlUpdate=new SimpleGdnDtl();
        List<DeliveryGoodsResponse> gdnDtlGoodsUpdate=new ArrayList<>();
        List<String> goodsSnUpdate=new ArrayList<>();
        BigDecimal pickQty;
        BigDecimal shipQty;
        BigDecimal orderQty;
        int updateDtlCode;
        for (Map.Entry<BigDecimal, List<DeliveryGoodsResponse>> entry : goodsCollectMap.entrySet()) {
            gdnDtlUpdate=dtlCollectMap.get(entry.getKey()).get(0);
            gdnDtlGoodsUpdate=entry.getValue();
            BigDecimal addQty = gdnDtlGoodsUpdate.stream().map(DeliveryGoodsResponse::getQty)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            pickQty=gdnDtlUpdate.getPickQty();
            shipQty=addQty.add(gdnDtlUpdate.getShipQty());
            orderQty=gdnDtlUpdate.getOrderQty();
            if (shipQty.compareTo(orderQty)==1){
                Shift.fatal("交接数大于需求数，无法交接!");
            }
            //必须拣货才能交接，因此明细状态必定为拣货中以后
            if ((shipQty.compareTo(orderQty)==0)&&(pickQty.compareTo(orderQty)==0)){
                updateDtlCode=GdnDtlStatusEnum.SHIPMENTED.code;
            }else if ((shipQty.compareTo(orderQty)==-1)&&(pickQty.compareTo(orderQty)==0)){
                updateDtlCode=GdnDtlStatusEnum.LOADING.code;
            } else if ((shipQty.compareTo(BigDecimal.ZERO)==0)&&(pickQty.compareTo(orderQty)==0)){
                updateDtlCode=GdnDtlStatusEnum.WAITLOAD.code;
            }else {
                updateDtlCode=GdnDtlStatusEnum.PICKING.code;
            }
            if (updateDtlCode==GdnDtlStatusEnum.SHIPMENTED.code){
                deliveryMapper.updateGdnDtl(entry.getKey(),updateDtlCode,loginName,new Date(),addQty,new Date());
            }else {
                deliveryMapper.updateGdnDtl(entry.getKey(),updateDtlCode,loginName,new Date(),addQty,null);
            }
            goodsSnUpdate=gdnDtlGoodsUpdate.stream().map(t -> t.getGoodsSn()).distinct().collect(Collectors.toList());
            deliveryMapper.updateGdnGoods(gdnDtlGoodsUpdate.get(0).getGdnDtlId(),
                    GdnDtlGoodsStatusEnum.OS_SHIP.code,goodsSnUpdate,loginName,new Date());
        }

        //查询主档的明细条数

        if (oneceNotLimitFlag){
            deliveryMapper.updateGdnHeader(gdnNo,GdnHeaderStatusEnum.FINISH.code,loginName,new Date(),new Date());
            //deliveryMapper.updateAllGdnDtl(gdnHeaderId,GdnDtlStatusEnum.SHIPMENTED.code,loginName,new Date(),new Date());
            //一次出库可少收的SN需要取消拣料
            List<GdnDtl> gdnDtlList = gdnDtlMapper.selectList(new QueryWrapper<GdnDtl>().eq("GDN_HEADER_ID", gdnHeaderId));
            List<Long> gdnDtlIds =gdnDtlList .stream().map(t -> t.getGdnDtlId()).collect(Collectors.toList());
            Map<Long, List<GdnDtl>> dtlMap = gdnDtlList.stream().collect(Collectors.groupingBy(GdnDtl::getGdnDtlId));
            List<GdnDtlGoods> dtlGoodsList = gdnDtlGoodsMapper.selectList(new QueryWrapper<GdnDtlGoods>().in("GDN_DTL_ID", gdnDtlIds).eq("STATUS", 1));
            if (!CollectionUtil.isEmpty(dtlGoodsList)){
                List<CancelPickMaterialRequest> cancelDtoList=new ArrayList<>();
                for (GdnDtlGoods gdnDtlGoods : dtlGoodsList) {
                    CancelPickMaterialRequest cancelDto=new CancelPickMaterialRequest();
                    GdnDtl cancelDtl = dtlMap.get(gdnDtlGoods.getGdnDtlId()).get(0);
                    cancelDto.setGdnHeaderId(cancelDtl.getGdnHeaderId());
                    cancelDto.setSkuId(cancelDtl.getSkuId());
                    cancelDto.setSn(gdnDtlGoods.getGoodsSn());
                    cancelDtoList.add(cancelDto);
                }
                cancelPick(cancelDtoList,servletRequest,servletResponse,true,null);
            }
        }else {
            Integer allCount = deliveryMapper.selectCountDtl(gdnHeaderId, null);
            //查询明细状态为取消，发运，关闭的条数
            Integer deliveryCount = deliveryMapper.selectCountDtl(gdnHeaderId, dtlStatusList);
            if (allCount.equals(deliveryCount)){
                deliveryMapper.updateGdnHeader(gdnNo,GdnHeaderStatusEnum.FINISH.code,loginName,new Date(),new Date());
            }
        }



//        Shift.fatal("/////////////");

        //清除缓存
        this.deleteCache(gdnNo);

        //调用SAP接口
        String gdnType = gdnHeader.getGdnType();
        //1：内销售后领料单；D14
        /*2：申购出库单：D15
        3:交货出库单
        4、报废出库单冻结
        4.1 报废单出库单过账 D16
        5、不同库位调拨
        6、工厂间调拨
        7、在途调拨
        8、标准委外领料出库D12
        9、标准委外退本厂*/
        switch (gdnType){
            case "D02" : //生产领料 一次  生产领退料
                getAndReturnMaterialBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap);
                break;
            case "D12" : //标准委外领料出库 出库单接口
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"8");
                break;
            case "D14": //内销售后 出库单接口
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"1");
                break;
            case "D15":  //申购出库 出库单接口
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"2");
                break;
            case "D16":  //报废出库 出库单接口
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"4.1");
                break;
            case "D17":  //报废销售 出库单接口
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"3");
                break;
            case "D18": //标准委外退本厂 采购订单接口
                purchaseOrderBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"1");
                break;
            case "D21": //易损件领料单
                getAndReturnMaterialBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap);
                break;
            case "D23" : //调拨出
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"5");
                break;
            case "D24" : //公司间交货出
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"3");
                break;
            case "D25" : //公司间退货出
                //outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"3");
                purchaseOrderBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"2");
                break;
            case "D26" : //公司间交货出入
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"3");
                break;
            case "D27" : //公司间退货出入 --待检入库
                //waitCheckBackService.waitCheckBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"3");
                purchaseOrderBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"2");
                break;
            case "D28" : //生产领料
                getAndReturnMaterialBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap);
                break;
            default:
                break;

        }

        return new Result(true,"交接成功!");
    }



    //获取交接确认主页
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public DeliveryMainResponse searchMainPage(String gdnNo){
        List<DeliveryHeaderResponse> deliveryHeaders = deliveryMapper.selectDeliveryHeader(gdnNo);
        if (CollectionUtil.isEmpty(deliveryHeaders)){
            Shift.fatal("没有找到对应出库单["+gdnNo+"]的信息!");
        }
        DeliveryHeaderResponse deliveryHeader = deliveryHeaders.get(0);
        if (deliveryHeader==null){
            Shift.fatal("没有找到对应出库单["+gdnNo+"]的信息!");
        }
        if (deliveryHeader.getHeaderStatus()==null){
            Shift.fatal("出库单["+gdnNo+"]状态错误");
        }
        if (deliveryHeader.getHeaderStatus().equals(GdnHeaderStatusEnum.CANCEL.code)){
            Shift.fatal("出库单["+gdnNo+"]状态为["+GdnHeaderStatusEnum.CANCEL.enumName+"],不可进行出库交接确认。");
        }
        if (deliveryHeader.getHeaderStatus().equals(GdnHeaderStatusEnum.FINISH.code)){
            Shift.fatal("出库单["+gdnNo+"]状态为["+GdnHeaderStatusEnum.FINISH.enumName+"],不可进行出库交接确认。");
        }
        if (deliveryHeader.getHeaderStatus().equals(GdnHeaderStatusEnum.CLOSE.code)){
            Shift.fatal("出库单["+gdnNo+"]状态为["+GdnHeaderStatusEnum.CLOSE.enumName+"],不可进行出库交接确认。");
        }
        GdnHeaderStatusEnum gdnHeaderStatusEnum = GdnHeaderStatusEnum.getEnumByCode(deliveryHeader.getHeaderStatus());
        if (gdnHeaderStatusEnum!=null){
            deliveryHeader.setHeaderStatusStr(gdnHeaderStatusEnum.enumName);
        }
        if (deliveryHeader.getGdnType()!=null){
            GdnTypeEnum gdnTypeEnum = GdnTypeEnum.getEnumByCode(deliveryHeader.getGdnType());
            if (gdnTypeEnum!=null){
                deliveryHeader.setGdnTypeStr(gdnTypeEnum.enumName);
            }
        }
        //查询已经交接的数量
        BigDecimal delivreryQty = deliveryMapper.selectDeliveryCount(deliveryHeader.getGdnHeaderId());
        //查询发货单需求总数
        BigDecimal orderQty = deliveryMapper.selectOrderyCount(deliveryHeader.getGdnHeaderId());
        if (delivreryQty.compareTo(orderQty)==0){
            Shift.fatal("该发货单已完全交接!");
        }
        //查询主页明细信息
        List<DeliveryDtlResponse> deliveryDtlList = this.searchDtlCommon(gdnNo, deliveryHeader.getGdnHeaderId());

        DeliveryMainResponse result=new DeliveryMainResponse();
        result.setHeader(deliveryHeader);
        result.setDtlList(deliveryDtlList);
        return result;
    }

    //主页面扫描SN后返回明细信息
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public List<DeliveryDtlResponse> scanSnDelivery(String gdnNo,BigDecimal gdnHeaderId,String goodsSn){
        if (goodsSn==null){
            Shift.fatal("请扫描条码!");
        }
        if (gdnHeaderId==null){
            Shift.fatal("请先扫描出库单号!");
        }

        //获取扫描的SN信息
        List<DeliveryGoodsResponse> deliveryGoods = deliveryMapper.selectOneDeliveryGoods(gdnHeaderId, goodsSn);
        if (CollectionUtil.isEmpty(deliveryGoods)){
            Shift.fatal("条码["+goodsSn+"]没有在出库单["+gdnNo+"]下!");
        }
        if (deliveryGoods.get(0).getStatusInt().equals(DeliveryScanStatusEnum.YES_CONFIRM.code)){
            Shift.fatal("条码["+goodsSn+"]已确认,不可重复扫描!");
        }
        if (!deliveryGoods.get(0).getPickStatus().equals(GdnDtlGoodsStatusEnum.IS_PICK.code)){
            Shift.fatal("条码["+goodsSn+"]状态非["+GdnDtlGoodsStatusEnum.IS_PICK.enumName+"]，不可出库!");
        }
        Goods goods = goodsMapper.selectBySn(goodsSn);
        if (goods==null){
            Shift.fatal("没有条码["+goodsSn+"]的信息!");
        }
        if (!goods.getStatus().equals(GoodsStatusEnum.HAS_DOWN.code)){
            Shift.fatal("条码["+goodsSn+"]状态非["+GoodsStatusEnum.HAS_DOWN.enumName+"]，不可出库!");
        }

        //获取缓存的条码信息
        List<String> goodsListCache = this.getGoodsListCache(gdnNo);
        if (!CollectionUtil.isEmpty(goodsListCache)){
            if (goodsListCache.contains(goodsSn)){
                Shift.fatal("SN["+goodsSn+"]已扫描，不可重复扫描!");
            }
        }else {
            goodsListCache=new ArrayList<>();
        }
        goodsListCache.add(goodsSn);
        this.insertUpdateGoodCache(gdnNo,goodsListCache);

        return searchDtlCommon(gdnNo,gdnHeaderId);
    }


    //点击明细进入SN详情页
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public List<DeliveryGoodsResponse> searchDeliveryGoods(String gdnNo, BigDecimal gdnHeaderId,BigDecimal skuId){
        if (gdnHeaderId==null){
            Shift.fatal("请扫描正确的出库条码!");
        }
        List<DeliveryGoodsResponse> results=new ArrayList<>();

        //查询返回信息
        List<DeliveryGoodsResponse> datas = deliveryMapper.selectDeliveryGoods(gdnHeaderId, skuId);
        //查询对应发货单明细的条码
        if (CollectionUtil.isEmpty(datas)){
            return null;
        }
        List<String> allGoodsSn = datas.stream().map(t -> t.getGoodsSn()).distinct().collect(Collectors.toList());
        List<Goods> allGoodsList = goodsMapper.selectList(new QueryWrapper<Goods>().in("GOODS_SN", allGoodsSn));
        List<String> exitSnList = allGoodsList.stream().map(t -> t.getGoodsSn()).distinct().collect(Collectors.toList());
        for (String allSn : allGoodsSn) {
            if (exitSnList.contains(allSn)){
                continue;
            }
            allGoodsList.add(goodsHisMapper.selectGoodsByHisSn(Arrays.asList(allSn)).get(0));
        }

        //获取缓存的条码
        List<String> goodsListCache = this.getGoodsListCache(gdnNo);

        Map<String, List<DeliveryGoodsResponse>> deliveryMap = datas.stream().collect(Collectors.groupingBy(DeliveryGoodsResponse::getGoodsSn));
        for (Goods goods : allGoodsList) {
            DeliveryGoodsResponse goodsResponse = deliveryMap.get(goods.getGoodsSn()).get(0);
            DeliveryGoodsResponse deliveryGoodsResponse=new DeliveryGoodsResponse();
            deliveryGoodsResponse.setQty(goods.getQty());
            deliveryGoodsResponse.setSkuId(goodsResponse.getSkuId());
            deliveryGoodsResponse.setSkuCode(goodsResponse.getSkuCode());
            deliveryGoodsResponse.setSkuId(goodsResponse.getSkuId());
            deliveryGoodsResponse.setDescCn(goodsResponse.getDescCn());
            deliveryGoodsResponse.setGoodsSn(goodsResponse.getGoodsSn());
            if (!CollectionUtil.isEmpty(goodsListCache)){
                if (goodsListCache.contains(goods.getGoodsSn())){
                    deliveryGoodsResponse.setStatusStr(DeliveryScanStatusEnum.YES_SCAN.enumName);
                }else {
                    deliveryGoodsResponse.setStatusStr(DeliveryScanStatusEnum.NO_SCAN.enumName);
                }
            }else {
                deliveryGoodsResponse.setStatusStr(DeliveryScanStatusEnum.NO_SCAN.enumName);
            }
            if (goodsResponse.getStatusInt().equals(DeliveryScanStatusEnum.YES_CONFIRM.code)){
                deliveryGoodsResponse.setStatusStr(DeliveryScanStatusEnum.YES_CONFIRM.enumName);
            }
            results.add(deliveryGoodsResponse);
        }


        return results;
    }

    //SN反扫描【POST请求】
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public List<DeliveryGoodsResponse> cancelSn(String gdnNo, BigDecimal gdnHeaderId,
                                                BigDecimal skuId,List<String> cancelSnList){
        if (CollectionUtil.isEmpty(cancelSnList)){
            Shift.fatal("请选择条码提交");
        }
        //查询取消的SN交接标志
        List<DeliveryGoodsResponse> deliveryFlagList = deliveryMapper.selectDeliveryGoodsList(gdnHeaderId, cancelSnList);
        for (DeliveryGoodsResponse deliveryFlag : deliveryFlagList) {
            if (deliveryFlag.getStatusInt().equals(DeliveryScanStatusEnum.YES_CONFIRM.code)){
                Shift.fatal("["+deliveryFlag.getGoodsSn()+"]已交接，无法取消扫描!");
            }
        }
        //获取缓存
        List<String> snCacheList = getGoodsListCache(gdnNo);
        if (CollectionUtil.isEmpty(snCacheList)){
            Shift.fatal("未存在扫描SN，无法反扫描!");
        }
        for (String cancelSn : cancelSnList) {
            if (!snCacheList.contains(cancelSn)){
                Shift.fatal("["+cancelSn+"]未扫描，无法反扫描!");
            }
        }
        List<String> newCacheSnList=new ArrayList<>();
        for (String snCache : snCacheList) {
            if (cancelSnList.contains(snCache)){
                continue;
            }
            newCacheSnList.add(snCache);
        }

        this.insertUpdateGoodCache(gdnNo,newCacheSnList);
        return searchDeliveryGoods(gdnNo,gdnHeaderId,skuId);
    }

    //根据缓存的SN信息计算主页明细的扫描数量
    private List<DeliveryDtlResponse> searchDtlCommon(String gdnNo, BigDecimal gdnHeaderId){
        List<DeliveryDtlResponse> result =deliveryMapper.selectDeliveryDtl(gdnHeaderId);
        if (CollectionUtil.isEmpty(result)){
            return result;
        }
        //查询发货单下已经确认的条码数量
        List<DeliveryScanQty> confirmQtys = deliveryMapper.selectConfirmQty(gdnHeaderId);
        Map<BigDecimal, List<DeliveryScanQty>> confirmMap = confirmQtys.stream().collect(Collectors.groupingBy(DeliveryScanQty::getSkuId));
        for (DeliveryDtlResponse dtlResponse : result) {
            List<DeliveryScanQty> confrimList = confirmMap.get(dtlResponse.getSkuId());
            if (CollectionUtil.isEmpty(confrimList)){
                dtlResponse.setDeliveryQtys(BigDecimal.ZERO);
                dtlResponse.setScanQtys(BigDecimal.ZERO);
            }else {
                dtlResponse.setDeliveryQtys(confrimList.get(0).getScanQty());
                dtlResponse.setScanQtys(confrimList.get(0).getScanQty());
            }
        }

        List<String> goodsSnCache = this.getGoodsListCache(gdnNo);
        //获取缓存的SN
        if (CollectionUtil.isEmpty(goodsSnCache)){
            return result;
        }
        //查询缓存SN中未确认的总数，作为扫描数
        List<DeliveryScanQty> scanQtys = deliveryMapper.selectScanQty(goodsSnCache);
        Map<BigDecimal, List<DeliveryScanQty>> scanQtyMap = scanQtys.stream().collect(Collectors.groupingBy(DeliveryScanQty::getSkuId));
        for (DeliveryDtlResponse dtlResponse : result) {
            List<DeliveryScanQty> scanQtyList = scanQtyMap.get(dtlResponse.getSkuId());
            if (CollectionUtil.isEmpty(scanQtyList)){
                continue;
            }
            dtlResponse.setScanQtys(dtlResponse.getScanQtys().add(scanQtyList.get(0).getScanQty()));
        }

        return result;
    }


    //根据gdnNo为key查询缓存
    public List<String> getGoodsListCache(String gdnNo){
        log.info("getDeliveryKey:{}", gdnNo);
        List<String> results=(List<String>) RedisUtil.get(gdnNo);
        return results;
    }

    //根据gdnNo为key添加/更新缓存
    public void insertUpdateGoodCache(String gdnNo,List<String> request){
        log.info("insertDeliveryKey:{}", gdnNo);
        RedisUtil.set(gdnNo,request);
    }

    //根据gdnNo为key删除缓存
    public void deleteCache(String gdnNo){
        log.info("deleteDeliveryKey:{}", gdnNo);
        RedisUtil.delete(gdnNo);
    }

    //生成入库凭证单头
    private TrsnHeader setTrsnHeader(GdnHeader header, SimpleGdnDtl dtl,String loginName){
        TrsnHeader result = new TrsnHeader();
        result.setNoteNo(basicSimpleCodeRuleApi.GenerateCode(TRSN_ORDER));
        result.setNoteType(TrsnNoteTypeEnum.OUT_STORAGE.getCode());
        result.setOwner(header.getOwner());
        result.setFactory(header.getFactory());
        result.setWhsId(dtl.getWhsId());
        result.setVendorId(dtl.getVendor());
        result.setFmDoc(header.getGdnNo());
        result.setCreateUser(loginName);
        result.setStatus(TrsnHeaderStatusEnum.CMP.code);
        result.setCreateTime(new Date());

        return result;
    }

    //生成入库凭证明细
    private TrsnDtl setTrsnDtl(GdnHeader gdnHeader, SimpleGdnDtl gdnDtl,
                               String loginName,TrsnHeader trsnHeader,int count){
        Date date = new Date();
        TrsnDtl trsnDtl=new TrsnDtl();
        trsnDtl.setTrsnHeaderId(trsnHeader.getTrsnHeaderId());
        trsnDtl.setNoteNo(trsnHeader.getNoteNo());
        trsnDtl.setNoteLineNo(""+count);
        trsnDtl.setFmDoc(gdnHeader.getGdnNo());
        trsnDtl.setFmDocLine(""+gdnDtl.getGdnLineNo());
        trsnDtl.setInvUpdateFlag(TrsnDtlInvUpdateFlagEnum.OUT.code);
        trsnDtl.setSkuId(gdnDtl.getSkuId());
        trsnDtl.setQty(gdnDtl.getOrderQty());
        trsnDtl.setUnit(gdnDtl.getUnit());
        trsnDtl.setInvAttr(gdnDtl.getInvAttr());
        trsnDtl.setInvType(gdnDtl.getInvType());
        trsnDtl.setQaStatus(gdnDtl.getQaStatus());
        trsnDtl.setLotAttr1(gdnDtl.getLotAttr1());
        trsnDtl.setLotAttr2(gdnDtl.getLotAttr2());
        trsnDtl.setLotAttr3(gdnDtl.getLotAttr3());
        trsnDtl.setLotAttr4(gdnDtl.getLotAttr4());
        trsnDtl.setLotAttr5(gdnDtl.getLotAttr5());
        trsnDtl.setLotAttr6(gdnDtl.getLotAttr6());
        trsnDtl.setLotAttr7(gdnDtl.getLotAttr7());
        trsnDtl.setLotAttr8(gdnDtl.getLotAttr8());
        trsnDtl.setLotAttr9(gdnDtl.getLotAttr9());
        trsnDtl.setLotAttr10(gdnDtl.getLotAttr10());
        trsnDtl.setLotAttr11(gdnDtl.getLotAttr11());
        trsnDtl.setLotAttr12(gdnDtl.getLotAttr12());
        trsnDtl.setStatus(TrsnDtlStatusEnum.CMP.code);
        trsnDtl.setCreateUser(loginName);
        trsnDtl.setCreateTime(date);
        return trsnDtl;
    }

    //生成库存信息
    private UpdateInventoryInputDto setInventoryInputDto(
            List<Goods> goodsList,String loginName){
        UpdateInventoryInputDto inventoryInputDto = new UpdateInventoryInputDto();
        BigDecimal qty = goodsList.stream().map(Goods::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);
        inventoryInputDto.setWhsId(goodsList.get(0).getWhsId());
        inventoryInputDto.setLocId(goodsList.get(0).getLocId());
        inventoryInputDto.setOwner(goodsList.get(0).getOwner());
        inventoryInputDto.setSkuId(goodsList.get(0).getSkuId());
        inventoryInputDto.setFactory(goodsList.get(0).getFactory());
        inventoryInputDto.setVendorId(goodsList.get(0).getVendorId());
        inventoryInputDto.setLotNo(goodsList.get(0).getLotno());
        inventoryInputDto.setInvAttr(goodsList.get(0).getInvAttr());
        inventoryInputDto.setQaStatus(goodsList.get(0).getQaStatus());
        inventoryInputDto.setOperator(loginName);
        inventoryInputDto.setQty(qty.negate());
        inventoryInputDto.setAlocQty(qty.negate());
        inventoryInputDto.setUnit(goodsList.get(0).getUnit());
        return inventoryInputDto;
    }

    //生成操作日志
    private GoodsLog setGoodsLog(String gdnNo,String loginName,Date logDate,Goods goods){
        GoodsLog goodsLog = new GoodsLog();
        goodsLog.setGoodsSn(goods.getGoodsSn());
        goodsLog.setOpCode(OpCodeEnum.SHIPPED.getCode());
        goodsLog.setReason("出库确认,出库单号["+gdnNo+"]");
        String goodsJson="未更新的:" + JSON.toJSONString(goods)+ ",更新的:{条码"+goods.getGoodsSn()+"删除移动到HIS表}";
        goodsLog.setUpdateContent(goodsJson);
        goodsLog.setOperator(loginName);
        goodsLog.setCreateUser(loginName);
        goodsLog.setCreateTime(logDate);
        goodsLog.setModifyUser(loginName);
        goodsLog.setModifyTime(logDate);
        return goodsLog;
    }

    //生成入库单头
    private void setAsnHeader(AsnHeader asnHeader,GdnHeader gdnHeader,String loginName,Date nowDate,
                              String gdnNo,String gdnType, List<DeliveryGoodsResponse> goods){
        String asnNo=" ";
        String asnType=" ";
        asnHeader.setToWhs(0);//TODO 待定
        if (gdnType.equals(GdnTypeEnum.D26.code)){
            asnNo = this.basicSimpleCodeRuleApi.GenerateCode("ASN_R19");
            asnType=AsnTypeEnum.R19.code;
            asnHeader.setQcStatus(QcStatusEnum.未创建质检单.getCode());
            asnHeader.setFactory(gdnHeader.getGdnRef2());//
        }else if (gdnType.equals(GdnTypeEnum.D27.code)){
            asnNo = this.basicSimpleCodeRuleApi.GenerateCode("ASN_R20");
            asnType=AsnTypeEnum.R20.code;
            asnHeader.setQcStatus(QcStatusEnum.全部质检.getCode());
            asnHeader.setFactory(gdnHeader.getGdnRef2());//
        }else if (gdnType.equals(GdnTypeEnum.D29.code)){
            asnNo = this.basicSimpleCodeRuleApi.GenerateCode("ASN_R22");
            asnType=AsnTypeEnum.R22.code;
            asnHeader.setQcStatus(QcStatusEnum.全部质检.getCode());
            asnHeader.setFactory(gdnHeader.getFactory());//
        }
        Goods goodsSn = goodsMapper.selectBySn(goods.get(0).getGoodsSn());
        asnHeader.setAsnType(asnType);
        asnHeader.setAsnNo(asnNo);
        asnHeader.setOwner(gdnHeader.getOwner());
        asnHeader.setAsnCreateTime(nowDate);
        asnHeader.setAsnCreateUser(loginName);
        asnHeader.setVendorId(goodsSn.getVendorId());
        asnHeader.setPoNo(goodsSn.getPoNo());
        asnHeader.setDataSource(gdnNo);
        asnHeader.setSourceNo(gdnNo);
        asnHeader.setReleaseStatus(1);
        asnHeader.setStatus(AsnHeaderStatusEnum.CREATE.code);
        asnHeader.setCreateUser(loginName);
        asnHeader.setCreateTime(nowDate);
    }

    //生成入库单明细
    private void setAsnDtl(AsnHeader asnHeader,AsnDtl asnDtl,int count,
                           String loginName,String gdnNo,Date nowDate,GdnDtl gdnDtl,GdnHeader gdnHeader){
        asnDtl.setAsnHeaderId(asnHeader.getAsnHeaderId());
        asnDtl.setAsnNo(asnHeader.getAsnNo());
        asnDtl.setAsnLineNo(count+"");
        asnDtl.setPoNo(gdnDtl.getPoNo());
        asnDtl.setSkuId(gdnDtl.getSkuId());
        if (gdnDtl.getSkuId()!=null){
            Sku sku = skuMapper.selectOne(new QueryWrapper<Sku>().eq("SKU_ID",gdnDtl.getSkuId()));
            if (sku!=null){
                asnDtl.setSkuDesc(sku.getDescCn());
                asnDtl.setSkuDescEn(sku.getDescEn());
            }
        }
        asnDtl.setSourceNo(gdnHeader.getGdnNo());
        asnDtl.setSourceLineNo(gdnDtl.getSourceShipNoticeItem());
        asnDtl.setLotno(gdnDtl.getLotno());
        asnDtl.setOrderQty(gdnDtl.getOrderQty());
        asnDtl.setOrderQtyEa(gdnDtl.getOrderQty());
        asnDtl.setUnit(gdnDtl.getUnit());
        asnDtl.setInvAttr(gdnDtl.getInvAttr());
        if (gdnDtl.getUdf5()!=null){
            asnDtl.setWhsId(Integer.valueOf(gdnDtl.getUdf5()));
        }
        asnDtl.setUdf5(gdnDtl.getWhsId()+"");
        asnDtl.setQcStatus(asnHeader.getQcStatus());
        if (asnHeader.getAsnType().equals(AsnTypeEnum.R19.code)){
            asnDtl.setQaStatus(QaStatusEnum.WAIT.code);
        }else if (asnHeader.getAsnType().equals(AsnTypeEnum.R20.code)){
            asnDtl.setQaStatus(QaStatusEnum.OK.code);
        }else if (asnHeader.getAsnType().equals(AsnTypeEnum.R22.code)){
            asnDtl.setQaStatus(QaStatusEnum.OK.code);
        }
        asnDtl.setStatus(AsnDtlStatusEnum.CREATE.code);
        asnDtl.setCreateUser(loginName);
        asnDtl.setCreateTime(nowDate);

    }

    public Result<List<ScanResponse>> cancelPick(List<CancelPickMaterialRequest> list,
                                                 HttpServletRequest servletRequest, HttpServletResponse servletResponse, Boolean flag, String systemName) throws Exception {
        String loginName = HttpSessionUtil.getLoginName(servletRequest);
        if (systemName!=null){
            loginName=systemName;
        }
        Date date = new Date();

        List<Integer> goodsIds = new ArrayList<>();
        for (CancelPickMaterialRequest item : list) {
            String sn = item.getSn();
            Long gdnHeaderId = item.getGdnHeaderId();
            Integer skuId = item.getSkuId();

            Goods goods = goodsMapper.selectBySn(sn);
            if (goods == null) {
                Shift.fatal("没有SN[" + sn + "]的信息!");
            }
            if (!goods.getStatus().equals(GoodsStatusEnum.HAS_DOWN.code)) {
                Shift.fatal("SN[" + goods.getGoodsSn() + "]状态非[" + GoodsStatusEnum.HAS_DOWN.enumName + "],不能取消拣选!");
            }

            List<Integer> dtlGoodsStatuList = pickTaskMapper.selectGdnDtlGoodsStatus(gdnHeaderId, sn);
            if ( CollectionUtil.isEmpty(dtlGoodsStatuList)){
                Shift.fatal("["+sn+"]状态非["+GdnDtlGoodsStatusEnum.IS_PICK.enumName+"]，无法取消拣货!");
            }
            if ( !dtlGoodsStatuList.get(0).equals(GdnDtlGoodsStatusEnum.IS_PICK.code)){
                Shift.fatal("["+sn+"]状态非["+GdnDtlGoodsStatusEnum.IS_PICK.enumName+"]，无法取消拣货!");
            }

            //查询发货单主档状态
            GdnHeader gdnHeader = gdnHeaderMapper.selectOne(new QueryWrapper<GdnHeader>().eq("GDN_HEADER_ID", gdnHeaderId));
            Integer headerStatus = gdnHeader.getStatus();

            List<String > cacheGoodsSnList = getGoodsListCache(gdnHeader.getGdnNo());
            if (!CollectionUtil.isEmpty(cacheGoodsSnList)){
                if (cacheGoodsSnList.contains(sn)){
                    Shift.fatal("SN["+sn+"]已交接扫描，请完成SN反扫描再取消拣料!");
                }
            }

            //查询取消拣货的明细信息
            List<CancelPickResponse> cancelPickResponses = pickTaskMapper.selectCancelPickInfo(gdnHeaderId, skuId, goods.getGoodsSn());

            String gdnLine = cancelPickResponses.stream().map(t -> t.getGoodsSn()).collect(Collectors.toList()).stream().collect(Collectors.joining(","));

            //更新货品表(wms_goods)
            Goods goodsUpdate = new Goods();
            goodsIds.add(goods.getGoodsId());
            goodsUpdate.setGoodsId(goods.getGoodsId());
            goodsUpdate.setModifyTime(new Date());
            goodsUpdate.setModifyUser(loginName);
            goodsUpdate.setStatus(GoodsStatusEnum.WAIT_DOWM.getCode());
            int rowGoods = goodsMapper.updateById(goodsUpdate);
            log.info("成功更新{}条记录", rowGoods);
            //更新货品表(wms_goods)

            //待减少的goods数量

            //2）更新发货单明细表(wms_gdn_dtl)
            //pick_qty(拣料数量)减少(分配给本发货单明细的数量)
            //pick_qty_ea(拣料数量ea)减少(分配给本发货单明细的数量)
            //status(状态)：
            //若pick_qty=0，则取1(新建)，
            //若pick_qty<order_qty且ship_qty=0,则取4(拣货中)，
            //若pick_qty=order_qty且ship_qty=0,则取5(待装柜)
            //
            //3）将对应SN从发货单单明细货品清单表(wms_gdn_dtl_goods)删除
            //创建类型的条件构造器

            //todo 若pick_qty=0，则取1(新建)，
            //todo 若pick_qty<order_qty且ship_qty=0,则取4(拣货中)，
            //todo 若pick_qty=order_qty且ship_qty=0,则取5(待装柜)

            //更新库存
            //库位
            UpdateInventoryInputDto inputLocDto = new UpdateInventoryInputDto();
            inputLocDto.setAlocQty(goods.getQty().negate());
            inputLocDto.setWhsId(goods.getWhsId());
            inputLocDto.setLocId(goods.getLocId());
            inputLocDto.setOwner(goods.getOwner());
            inputLocDto.setSkuId(goods.getSkuId());
            inputLocDto.setFactory(goods.getFactory());
            inputLocDto.setVendorId(goods.getVendorId());
            inputLocDto.setLotNo(goods.getLotno());
            inputLocDto.setInvAttr(goods.getInvAttr());
            inputLocDto.setQaStatus(goods.getQaStatus());
            inputLocDto.setUnit(goods.getUnit());
            inputLocDto.setOperator(loginName);
            inventoryService.updateLocInventroy(inputLocDto);

            //库区
            inventoryService.updateZoneInventory(inputLocDto);

            if (goods.getLocId()!=null){
                // 如SN原库位不为空，扣除原库位库存，添加空库位库存
                UpdateInventoryInputDto inputLocDtoSub = new UpdateInventoryInputDto();
                inputLocDtoSub.setQty(goods.getQty().negate());
                inputLocDtoSub.setWhsId(goods.getWhsId());
                inputLocDtoSub.setLocId(goods.getLocId());
                inputLocDtoSub.setOwner(goods.getOwner());
                inputLocDtoSub.setSkuId(goods.getSkuId());
                inputLocDtoSub.setFactory(goods.getFactory());
                inputLocDtoSub.setVendorId(goods.getVendorId());
                inputLocDtoSub.setLotNo(goods.getLotno());
                inputLocDtoSub.setInvAttr(goods.getInvAttr());
                inputLocDtoSub.setQaStatus(goods.getQaStatus());
                inputLocDtoSub.setUnit(goods.getUnit());
                inputLocDtoSub.setOperator(loginName);

                UpdateInventoryInputDto inputLocDtoAdd = new UpdateInventoryInputDto();
                inputLocDtoAdd.setQty(goods.getQty());
                inputLocDtoAdd.setWhsId(goods.getWhsId());
                inputLocDtoAdd.setLocId(null);
                inputLocDtoAdd.setOwner(goods.getOwner());
                inputLocDtoAdd.setSkuId(goods.getSkuId());
                inputLocDtoAdd.setFactory(goods.getFactory());
                inputLocDtoAdd.setVendorId(goods.getVendorId());
                inputLocDtoAdd.setLotNo(goods.getLotno());
                inputLocDtoAdd.setInvAttr(goods.getInvAttr());
                inputLocDtoAdd.setQaStatus(goods.getQaStatus());
                inputLocDtoAdd.setUnit(goods.getUnit());
                inputLocDtoAdd.setOperator(loginName);

                InvTrans invTrans = new InvTrans();
                invTrans.setOperator(loginName);
                invTrans.setCreateUser(loginName);
                invTrans.setCreateTime(new Date());
                invTrans.setOpCode(OperateCodeEnum.CREATE.getEnumName());// TODO
                // 操作
                invTrans.setDocType(cancelPickResponses.get(0).getGdnType());// 单据类型
                invTrans.setDocNo(cancelPickResponses.get(0).getGdnNo());
                invTrans.setDocLineNo(gdnLine);
                invTrans.setStatus(InvTransStatusEnum.FINISHED.getCode()); // TODO
                // 0
                // 未完成，暂时写死
                // 交易状态
                invTrans.setTransTime(new Date());// TODO 交易时间
                invTrans.setOperator(loginName);
                invTrans.setFmOwner(goods.getOwner());// TODO 货主
                invTrans.setFmSkuId(goods.getSkuId());
                invTrans.setFmLocId(goods.getLocId());
                invTrans.setFmWhsId(goods.getWhsId());
                invTrans.setFmUnit(goods.getUnit());
                invTrans.setFmQty(goods.getQty());
                invTrans.setToOwner(goods.getOwner());
                invTrans.setToSkuId(goods.getSkuId());
                invTrans.setToLocId(null);
                invTrans.setToWhsId(goods.getWhsId());
                invTrans.setToQty(goods.getQty());
                // 更新库存
                inventoryService.inventoryTransfer(inputLocDtoSub, inputLocDtoAdd, invTrans);
            }

            //更新出库明细状态
            for (CancelPickResponse cancal : cancelPickResponses) {
                BigDecimal shipQty = cancal.getDtlShipQty();
                BigDecimal orderQty = cancal.getDtlOrderQty();
                BigDecimal pickQty = cancal.getDtlPickQty().subtract(cancal.getGoodsSnQty());
                if (cancal.getDtlPickQty().compareTo(cancal.getGoodsSnQty()) == -1) {
                    Shift.fatal("取消SN数量不能大于当前发货明细的拣货数!");
                }
                if (!(headerStatus.equals(GdnHeaderStatusEnum.CLOSE.code)||headerStatus.equals(GdnHeaderStatusEnum.FINISH.code))){
                    Integer updateStatus=null;
                    if (pickQty.compareTo(BigDecimal.ZERO) == 0){
                        updateStatus = GdnDtlStatusEnum.CREATE.code;
                    }
                    if (shipQty.compareTo(BigDecimal.ZERO) == 0) {
                        if (pickQty.compareTo(orderQty) == 0) {
                            updateStatus = GdnDtlStatusEnum.WAITLOAD.code;
                        } else if (pickQty.compareTo(BigDecimal.ZERO) == 0) {
                            updateStatus = GdnDtlStatusEnum.CREATE.code;
                        } else {
                            updateStatus = GdnDtlStatusEnum.PICKING.code;
                        }
                    }
                    if (!flag){
                        updateStatus = GdnDtlStatusEnum.CANCEL.code;
                    }
                    if (updateStatus!=null){
                        pickTaskMapper.updateGdnDtlStatus(cancal.getGdnDtlId(), updateStatus, cancal.getGoodsSnQty());
                    }
                } else {
                    pickTaskMapper.updateGdnDtlQty(cancal.getGdnDtlId(),cancal.getGoodsSnQty());
                }
            }

            if (!(headerStatus.equals(GdnHeaderStatusEnum.CLOSE.code)||headerStatus.equals(GdnHeaderStatusEnum.FINISH.code))){
                // 更新出库主档状态
                PickAllQty pickAllQty = pickTaskMapper.selectAllQty(gdnHeaderId);
                Integer updateHeaderStatus;
                // 待修改
                if (pickAllQty.getShipQty().compareTo(BigDecimal.ZERO) == 0) {
                    if (pickAllQty.getPickQty().compareTo(pickAllQty.getOrderQty()) == 0) {
                        updateHeaderStatus = GdnHeaderStatusEnum.DOING.code;
                        pickTaskMapper.updateGdnHeaderStatus(gdnHeaderId, updateHeaderStatus);
                    } else {
                        updateHeaderStatus = GdnHeaderStatusEnum.DOING.code;
                        pickTaskMapper.updateGdnHeaderStatus(gdnHeaderId, updateHeaderStatus);
                    }
                }
                if (pickAllQty.getPickQty().compareTo(BigDecimal.ZERO) == 0) {
                    updateHeaderStatus = GdnHeaderStatusEnum.CREATE.code;
                    pickTaskMapper.updateGdnHeaderStatus(gdnHeaderId, updateHeaderStatus);
                }
            }

            // 删除出库单货品
            List<BigDecimal> dtlGoodsIds = cancelPickResponses.stream().map(t -> t.getGdnDtlGoodsId()).collect(Collectors.toList());
            gdnDtlGoodsMapper.delete(new QueryWrapper<GdnDtlGoods>().in("GDN_DTL_GOODS_ID",dtlGoodsIds));

            //日志
            GoodsLog goodsLog = new GoodsLog();
            Goods goodsUp = goodsMapper.selectBySn(sn);
            goodsUp.setLocId(null);
            goodsLog.setGoodsSn(sn);
            goodsLog.setComments(GoodsLogEnum.RAW_UN_PICK.getEnumName());
            goodsLog.setReason(GoodsLogEnum.RAW_UN_PICK.getEnumName());
            goodsLog.setOpCode(GoodsLogEnum.RAW_UN_PICK.getCode());
            goodsLog.setOperator(loginName);
            goodsLog.setCreateTime(date);
            goodsLog.setCreateUser(loginName);
            goodsLog.setUpdateContent("未更新:" + JSON.toJSONString(goods) + ",更新后:" + JSON.toJSONString(goodsUp));
            goodsLogMapper.insert(goodsLog);
        }
        pickTaskMapper.updateGoodsLoc(goodsIds);

        // 调用SAP接口【取消拣货--->单子的所有条码都清除完毕的时候调用】
        if (flag){
            GdnHeader gdnHeader = gdnHeaderMapper.selectById(list.get(0).getGdnHeaderId());
            int goodsSnSize = gdnDtlGoodsMapper.selectCountSnGdn(gdnHeader.getGdnHeaderId());
            if (goodsSnSize == 0) {
                try {
                    ZDT_076_ReqItems record = new ZDT_076_ReqItems();
                    record.setBILLTYPE("1"); 			// 默认1
                    record.setBILLNO(gdnHeader.getGdnNo());
                    record.setZDATE(DateUtil.format(date, "yyyyMMdd"));
                    record.setZTIME(DateUtil.format(date, "HHmmss"));
                    record.setSTATUS("2"); 				// 1确认、2取消
                    record.setZNAME(loginName);
                    wmsBillStatusBackService.backToSap(record);
                } catch (SystemException e) {
                    Shift.fatal(e.getMessage());
                } catch (Exception e) {
                    Shift.fatal("调用SAP接口失败");
                }
            }
        }
//		Shift.fatal("/////////////////");
        return new Result<>();
    }




    /*
       交接确认提交功能：前段验证扫描数是否与订单数相等
       1.发货单头状态为0(取消)或4(已完成)或5(已关闭)，报错
       2.根据gdnNo获取缓存的要交接确认的SN信息
       3.发货单所有明细的状态0(取消)或7(已发运)或8(已关闭)，报错
       4.交接功能
           （1）生成入库凭证单头
           （2）生成入库单明细+货品
           （3）更新库位库存&库区库存
           （4）写SN操作日志
           （5）将SN从GOODS表删除移到GOODS_HIS表【置空WHS_ID&LOC_ID】
           （6）更新发货单主档状态为4(已完成)
           （7）更新发货单明细的发货数为订单数，状态为4(已完成)
           （8）更新发货单货品状态为2(已出库)
           （9）清除缓存

       TODO 特定单据类型出库是自动生成入库单
    */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result<?> deliveryCommit( BigDecimal gdnHeaderId,
                                     HttpServletRequest servletRequest, HttpServletResponse servletResponse)
            throws Exception{
        GdnHeader gdnHeader = gdnHeaderMapper.selectOne(new QueryWrapper<GdnHeader>(new GdnHeader().setGdnHeaderId(gdnHeaderId.longValue())));
        //判断订单能否提交
        Result result = checkPermission(gdnHeaderId);
        if(!result.isFlag()){
            Shift.fatal("该订单不能直接拣料出库，请检查系统isRawOutSure参数，和订单标识");
        }



        if (gdnHeader==null){
            Shift.fatal("请选择正确的出库单!");
        }
        String gdnNo = gdnHeader.getGdnNo();
        if (gdnHeader.getStatus()==null){
            Shift.fatal("出库单["+gdnNo+"]状态错误,不可进行出库!");
        }
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CANCEL.code)){
            Shift.fatal("出库单["+gdnNo+"]状态为["+GdnHeaderStatusEnum.CANCEL.enumName+"],不可进行出库!");
        }
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.FINISH.code)){
            Shift.fatal("出库单["+gdnNo+"]状态为["+GdnHeaderStatusEnum.FINISH.enumName+"],不可进行出库!");
        }
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CLOSE.code)){
            Shift.fatal("出库单["+gdnNo+"]状态为["+GdnHeaderStatusEnum.CLOSE.enumName+"],不可进行出库!");
        }

        //获取本次提交的条码(dtlGoods状态为已拣货1)
        List<String > snCacheList = gdnDtlGoodsMapper.selectByHeaderIdAndDtlGoodsStatus(gdnHeader.getGdnHeaderId(),GdnDtlGoodsStatusEnum.IS_PICK.code);
        //List<String> snCacheList = this.getGoodsListCache(gdnNo);
        if (CollectionUtil.isEmpty(snCacheList)){
            Shift.fatal("请扫描SN再提交！");
        }
        //查询条码信息
        List<Goods> goodsList = goodsMapper.selectList(new QueryWrapper<Goods>().in("GOODS_SN", snCacheList));
        if (CollectionUtil.isEmpty(goodsList)){
            Shift.fatal("请扫描正确的SN！");
        }
        if (snCacheList.size()!=goodsList.size()){
            Shift.fatal("请扫描正确的SN！");
        }
        for (Goods goods : goodsList) {
            if (!goods.getStatus().equals(GoodsStatusEnum.HAS_DOWN.code)){
                Shift.fatal(goods.getGoodsSn()+"状态非["+GoodsStatusEnum.HAS_DOWN.enumName+"]，无法出库!");
            }

        }

        Boolean oneceLimitFlag=false;//true为需要限制一次出库不允许短收
        Boolean oneceNotLimitFlag=false;//true为需要限制一次出库允许短收，短收后同时更新入库单主动明细为完成
        Boolean asnFlag=false;//true需要生成入库单

        switch (gdnHeader.getGdnType()){
            case "D12" : //委外领料 一次过账
                oneceNotLimitFlag=true;
                break;
            case "D16": //报废出库 一次过账
                oneceNotLimitFlag=true;
                break;
            case "D17":  //销售出库(供应商) 一次过账
                oneceNotLimitFlag=true;
                break;
            case "D21": //易损件领料单 一次过账
                oneceNotLimitFlag=true;
                break;
            case "D22": //不合格凭证退货 一次过账
                oneceLimitFlag=true;
                break;
            case "D29" : //不同库位调拨(出) 一次过账
                oneceNotLimitFlag=true;
                asnFlag=true;
                break;

            case "D26" : //不同库位调拨(出) 一次过账同时生成入库单
                asnFlag=true;
                oneceLimitFlag=true;
                break;
            case "D27" : //生产领料(单次) 一次过账同时生成入库单
                asnFlag=true;
                oneceLimitFlag=true;
                break;
            case "D28" : //生产领料(单次) 一次过账
                oneceNotLimitFlag=true;
                break;

            //TODO 新加的
            case "D24" : //不同库位调拨(出) 一次过账同时生成入库单
                oneceLimitFlag=true;
                break;
            case "D25" : //生产领料(单次) 一次过账同时生成入库单
                oneceLimitFlag=true;
                break;
        }

        //校验是否能一次过账
        if (oneceLimitFlag){
            //查询所有的明细行
            BigDecimal commitQty = goodsList.stream().map(Goods::getQty)
                    .reduce(BigDecimal.ZERO,BigDecimal::add);
            List<GdnDtl> dtlList = gdnDtlMapper.selectList(new QueryWrapper<GdnDtl>().eq("GDN_HEADER_ID", gdnHeader.getGdnHeaderId()));
            BigDecimal orderQty = dtlList.stream().map(GdnDtl::getOrderQty)
                    .reduce(BigDecimal.ZERO,BigDecimal::add);
            if(commitQty.compareTo(orderQty) != 0){
                Shift.fatal("出库数需要等于需求数!");
            }
        }



        //查询交接的GDN_DTL_GOODS
        List<DeliveryGoodsResponse> gdnDtlGoodsList = deliveryMapper.selectDeliveryGoodsList(gdnHeaderId, snCacheList);
        List<BigDecimal> dtlIdList = gdnDtlGoodsList.stream().map(t -> t.getGdnDtlId()).distinct().collect(Collectors.toList());
        //查询交接的GDN_DTL`
        List<SimpleGdnDtl> gdnDtls = deliveryMapper.selectGdnDtlList(gdnHeaderId,dtlIdList);
        for (SimpleGdnDtl gdnDtl : gdnDtls) {
            if (gdnDtl.getStatus()==null){
                Shift.fatal("出库单["+gdnNo+"]明细行号["+gdnDtl.getGdnLineNo()+"]、料号["+gdnDtl.getSkuCode()
                        +"]状态错误，不可进行出库!");
            }
            if (gdnDtl.getStatus().equals(GdnDtlStatusEnum.CANCEL.code)){
                Shift.fatal("出库单["+gdnNo+"]明细行号["+gdnDtl.getGdnLineNo()+"]、料号["+gdnDtl.getSkuCode()
                        +"]状态为["+GdnDtlStatusEnum.CANCEL.enumName+"]，不可进行出库!");
            }
            if (gdnDtl.getStatus().equals(GdnDtlStatusEnum.SHIPMENTED.code)){
                Shift.fatal("出库单["+gdnNo+"]明细行号["+gdnDtl.getGdnLineNo()+"]、料号["+gdnDtl.getSkuCode()
                        +"]状态为["+GdnDtlStatusEnum.SHIPMENTED.enumName+"]，不可进行出库!");
            }
            if (gdnDtl.getStatus().equals(GdnDtlStatusEnum.CLOSE.code)){
                Shift.fatal("出库单["+gdnNo+"]明细行号["+gdnDtl.getGdnLineNo()+"]、料号["+gdnDtl.getSkuCode()
                        +"]状态为["+GdnDtlStatusEnum.CLOSE.enumName+"]，不可进行出库!");
            }
        }
        String loginName = HttpSessionUtil.getLoginName(servletRequest);

        Map<String, List<Goods>> trsnGoodsMap = goodsList.stream().collect(Collectors.groupingBy(Goods::getGoodsSn));
        Map<BigDecimal, List<DeliveryGoodsResponse>> trsnDtlGoodsMap = gdnDtlGoodsList.stream().collect(Collectors.groupingBy(DeliveryGoodsResponse::getGdnDtlId));

        //记录出库凭证
        Date trsnDate = new Date();
        TrsnHeader trsnHeader = this.setTrsnHeader(gdnHeader, gdnDtls.get(0), loginName);
        trsnHeaderMapper.insert(trsnHeader);
        TrsnDtl trsnDtl=new TrsnDtl();
        TrsnDtlGoods trsnDtlGoods=new TrsnDtlGoods();
        String trsnGoodsSn;
        Goods trsnGoods=new Goods();
        int count=0;
        for (SimpleGdnDtl gdnDtl : gdnDtls) {
            List<DeliveryGoodsResponse> gdnDtlGoods = trsnDtlGoodsMap.get(gdnDtl.getGdnDtlId());
            trsnDtl = this.setTrsnDtl(gdnHeader, gdnDtl, loginName, trsnHeader,count);
            trsnDtl.setQty(gdnDtlGoods.stream()
                    .map(DeliveryGoodsResponse::getQty)
                    .reduce(BigDecimal.ZERO,BigDecimal::add));
            trsnDtlMapper.insert(trsnDtl);
            for (DeliveryGoodsResponse gdnDtlGood : gdnDtlGoods) {
                trsnGoodsSn=gdnDtlGood.getGoodsSn();
                trsnGoods=trsnGoodsMap.get(trsnGoodsSn).get(0);
                trsnDtlGoods.setTrsnDtlId(trsnDtl.getTrsnDtlId());
                trsnDtlGoods.setGoodsSn(trsnGoodsSn);
                trsnDtlGoods.setGoodsId(trsnGoods.getGoodsId());
                trsnDtlGoods.setQty(gdnDtlGood.getQty());
                trsnDtlGoods.setQaStatus(trsnGoods.getQaStatus());
                trsnDtlGoods.setComments("["+gdnNo+"]下明细行["+gdnDtl.getGdnDtlId()+"]出库");
                trsnDtlGoods.setStatus(trsnGoods.getStatus());
                trsnDtlGoods.setCreateUser(loginName);
                trsnDtlGoods.setCreateTime(trsnDate);
                trsnDtlGoods.setModifyUser(loginName);
                trsnDtlGoods.setModifyTime(trsnDate);
                trsnDtlGoodsMapper.insert(trsnDtlGoods);
            }
            count++;
        }

        //减少库存
        Map<String, List<Goods>> updateInvMap = goodsList.stream().collect(
                Collectors.groupingBy(t -> t.getWhsId() + "#" + t.getLocId() + "#" + t.getOwner() + "#" +
                        t.getSkuId() + "#" + t.getFactory() + "#" + t.getVendorId() + "#" + t.getLotno()
                        + "#" + t.getInvAttr() + "#" + t.getQaStatus(), Collectors.toList()));
        UpdateInventoryInputDto inventoryInputDto= new UpdateInventoryInputDto();
        for (Map.Entry<String, List<Goods>> entry : updateInvMap.entrySet()) {
            inventoryInputDto = this.setInventoryInputDto(entry.getValue(), loginName);
            inventoryService.updateLocInventroy(inventoryInputDto);
            inventoryService.updateZoneInventory(inventoryInputDto);
        }

        Date logDate=new Date();
        //将goods移入历史表并记录日志
        GoodsLog goodsLog=new GoodsLog();
        GoodsHis goodsHis=new GoodsHis();
        for (Goods goods : goodsList) {
            //写SN操作日志
            goodsLog= this.setGoodsLog(gdnNo, loginName, logDate, goods);
            goodsLogMapper.insert(goodsLog);
            //添加SN到HIS表
            BeanUtil.copyNonNull(goods, goodsHis);
            goodsHis.setLogCreateUser(loginName);
            goodsHis.setLogCreateTime(logDate);
            goodsHis.setStatus(GoodsStatusEnum.HAS_CHUKU.code);
            goodsHis.setWhsId(null);
            goodsHis.setLocId(null);
            goodsHis.setModifyTime(new Date());
            goodsHis.setModifyUser(loginName);
            goodsHisMapper.insert(goodsHis);
        }
        //TODO 待确定单据类型修改判断逻辑
        //TODO 入库单仓库逻辑待定&入库单类型待定
        if (asnFlag){
            List<DeliveryGoodsResponse> asndtlGoods=new ArrayList<>();
            AsnHeader asnHeader=new AsnHeader();
            List<GdnDtl> asnGdnList = gdnDtlMapper.selectList(new QueryWrapper<GdnDtl>().in("GDN_DTL_ID", dtlIdList));
            AsnDtl asnDtl=new AsnDtl();
            int dtlCount=1;
            Date asnDate=new Date();
            setAsnHeader(asnHeader,gdnHeader,loginName,new Date(),gdnNo,gdnHeader.getGdnType(),
                    trsnDtlGoodsMap.get(gdnDtls.get(0).getGdnDtlId()));
            asnHeaderMapper.insert(asnHeader);
            for (GdnDtl gdnDtl : asnGdnList) {
                setAsnDtl(asnHeader,asnDtl,dtlCount,loginName,gdnNo,asnDate,gdnDtl,gdnHeader);
                asnDtlMapper.insert(asnDtl);
                dtlCount++;
            }


        }

        //删除GOODS表的条码
        List<Integer> deleteGoodsIds = goodsList.stream().map(t -> t.getGoodsId()).collect(Collectors.toList());
        QueryWrapper<Goods> delectGoodsQueryWrapper=new QueryWrapper<>();
        delectGoodsQueryWrapper.in("GOODS_ID",deleteGoodsIds);
        goodsMapper.delete(delectGoodsQueryWrapper);

        //更新出库主档、明细、货品状态【待修改】
        Map<BigDecimal, List<SimpleGdnDtl>> dtlCollectMap = gdnDtls.stream().collect(Collectors.groupingBy(SimpleGdnDtl::getGdnDtlId));
        Map<BigDecimal, List<DeliveryGoodsResponse>> goodsCollectMap = gdnDtlGoodsList.stream().collect(Collectors.groupingBy(DeliveryGoodsResponse::getGdnDtlId));
        SimpleGdnDtl gdnDtlUpdate=new SimpleGdnDtl();
        List<DeliveryGoodsResponse> gdnDtlGoodsUpdate=new ArrayList<>();
        List<String> goodsSnUpdate=new ArrayList<>();
        BigDecimal pickQty;
        BigDecimal shipQty;
        BigDecimal orderQty;
        int updateDtlCode;
        for (Map.Entry<BigDecimal, List<DeliveryGoodsResponse>> entry : goodsCollectMap.entrySet()) {
            gdnDtlUpdate=dtlCollectMap.get(entry.getKey()).get(0);
            gdnDtlGoodsUpdate=entry.getValue();
            BigDecimal addQty = gdnDtlGoodsUpdate.stream().map(DeliveryGoodsResponse::getQty)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            pickQty=gdnDtlUpdate.getPickQty();
            shipQty=addQty.add(gdnDtlUpdate.getShipQty());
            orderQty=gdnDtlUpdate.getOrderQty();
            if (shipQty.compareTo(orderQty)==1){
                Shift.fatal("交接数大于需求数，无法交接!");
            }
            //必须拣货才能交接，因此明细状态必定为拣货中以后
            if ((shipQty.compareTo(orderQty)==0)&&(pickQty.compareTo(orderQty)==0)){
                updateDtlCode=GdnDtlStatusEnum.SHIPMENTED.code;
            }else if ((shipQty.compareTo(orderQty)==-1)&&(pickQty.compareTo(orderQty)==0)){
                updateDtlCode=GdnDtlStatusEnum.LOADING.code;
            } else if ((shipQty.compareTo(BigDecimal.ZERO)==0)&&(pickQty.compareTo(orderQty)==0)){
                updateDtlCode=GdnDtlStatusEnum.WAITLOAD.code;
            }else {
                updateDtlCode=GdnDtlStatusEnum.PICKING.code;
            }
            if (updateDtlCode==GdnDtlStatusEnum.SHIPMENTED.code){
                deliveryMapper.updateGdnDtl(entry.getKey(),updateDtlCode,loginName,new Date(),addQty,new Date());
            }else {
                deliveryMapper.updateGdnDtl(entry.getKey(),updateDtlCode,loginName,new Date(),addQty,null);
            }
            goodsSnUpdate=gdnDtlGoodsUpdate.stream().map(t -> t.getGoodsSn()).distinct().collect(Collectors.toList());
            deliveryMapper.updateGdnGoods(gdnDtlGoodsUpdate.get(0).getGdnDtlId(),
                    GdnDtlGoodsStatusEnum.OS_SHIP.code,goodsSnUpdate,loginName,new Date());
        }

        //查询主档的明细条数

        if (oneceNotLimitFlag){
            deliveryMapper.updateGdnHeader(gdnNo,GdnHeaderStatusEnum.FINISH.code,loginName,new Date(),new Date());
            //deliveryMapper.updateAllGdnDtl(gdnHeaderId,GdnDtlStatusEnum.SHIPMENTED.code,loginName,new Date(),new Date());
            //一次出库可少收的SN需要取消拣料
            List<GdnDtl> gdnDtlList = gdnDtlMapper.selectList(new QueryWrapper<GdnDtl>().eq("GDN_HEADER_ID", gdnHeaderId));
            List<Long> gdnDtlIds =gdnDtlList .stream().map(t -> t.getGdnDtlId()).collect(Collectors.toList());
            Map<Long, List<GdnDtl>> dtlMap = gdnDtlList.stream().collect(Collectors.groupingBy(GdnDtl::getGdnDtlId));
            List<GdnDtlGoods> dtlGoodsList = gdnDtlGoodsMapper.selectList(new QueryWrapper<GdnDtlGoods>().in("GDN_DTL_ID", gdnDtlIds).eq("STATUS", 1));
            if (!CollectionUtil.isEmpty(dtlGoodsList)){
                List<CancelPickMaterialRequest> cancelDtoList=new ArrayList<>();
                for (GdnDtlGoods gdnDtlGoods : dtlGoodsList) {
                    CancelPickMaterialRequest cancelDto=new CancelPickMaterialRequest();
                    GdnDtl cancelDtl = dtlMap.get(gdnDtlGoods.getGdnDtlId()).get(0);
                    cancelDto.setGdnHeaderId(cancelDtl.getGdnHeaderId());
                    cancelDto.setSkuId(cancelDtl.getSkuId());
                    cancelDto.setSn(gdnDtlGoods.getGoodsSn());
                    cancelDtoList.add(cancelDto);
                }
                cancelPick(cancelDtoList,servletRequest,servletResponse,true,null);
            }
        }else {
            Integer allCount = deliveryMapper.selectCountDtl(gdnHeaderId, null);
            //查询明细状态为取消，发运，关闭的条数
            Integer deliveryCount = deliveryMapper.selectCountDtl(gdnHeaderId, dtlStatusList);
            if (allCount.equals(deliveryCount)){
                deliveryMapper.updateGdnHeader(gdnNo,GdnHeaderStatusEnum.FINISH.code,loginName,new Date(),new Date());
            }
        }



//        Shift.fatal("/////////////");


        //调用SAP接口
        String gdnType = gdnHeader.getGdnType();
        //1：内销售后领料单；D14
        /*2：申购出库单：D15
        3:交货出库单
        4、报废出库单冻结
        4.1 报废单出库单过账 D16
        5、不同库位调拨
        6、工厂间调拨
        7、在途调拨
        8、标准委外领料出库D12
        9、标准委外退本厂*/
        switch (gdnType){
            case "D02" : //生产领料 一次  生产领退料
                getAndReturnMaterialBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap);
                break;
            case "D12" : //标准委外领料出库 出库单接口
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"8");
                break;
            case "D14": //内销售后 出库单接口
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"1");
                break;
            case "D15":  //申购出库 出库单接口
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"2");
                break;
            case "D16":  //报废出库 出库单接口
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"4.1");
                break;
            case "D17":  //报废销售 出库单接口
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"3");
                break;
            case "D18": //标准委外退本厂 采购订单接口
                purchaseOrderBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"1");
                break;
            case "D21": //易损件领料单
                getAndReturnMaterialBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap);
                break;
            case "D23" : //调拨出
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"5");
                break;
            case "D24" : //公司间交货出
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"3");
                break;
            case "D25" : //公司间退货出
                //outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"3");
                purchaseOrderBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"2");
                break;
            case "D26" : //公司间交货出入
                outStoreBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"3");
                break;
            case "D27" : //公司间退货出入 --待检入库
                //waitCheckBackService.waitCheckBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"3");
                purchaseOrderBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap,"2");
                break;
            case "D28" : //生产领料
                getAndReturnMaterialBackService.gdnWriteBackToSap(gdnHeader,dtlCollectMap,goodsCollectMap);
                break;
            default:
                break;

        }

        return new Result(true,"交接成功!");
    }



    @Reference(version = "${dubbo.providers.basic.BasicParam.api:1.0.0}", check = false, timeout = 300000)
    BasicParamApi basicParamApi;

    public Boolean getBasicParam(String  paramCode){
        ParamDetailDTO jit_isCheckComplete = basicParamApi.getByCode(paramCode);
        if (jit_isCheckComplete!=null && jit_isCheckComplete.getSystemParamValue()!=null){
            String systemParamValue = jit_isCheckComplete.getSystemParamValue();
            if (systemParamValue.equals("true")){
                return true;
            }else {
                return false;
            }
        }
        return true;
    }


    public  Result checkPermission(BigDecimal gdnHeaderId){
        Result result = new Result();
        Boolean isRawOutSure = getBasicParam("isRawOutSure");
        GdnHeader gdnHeader = gdnHeaderMapper.selectById(gdnHeaderId);
        if(gdnHeader.getNeedSure()==null){//没有标识以系统参数为准
            result.setFlag(!isRawOutSure);
        }else{
            //有标识以标识为准
            if(gdnHeader.getNeedSure()==NeedSureEnum.NOT_NEED.code){
                result.setFlag(true);
            }else{
                result.setFlag(false);
            }
        }
        return  result;
    }
}
